/**
 * @method Kxui   模块管理方法
 * @author Lkx
 *
 * @method use    模块加载
 * @method config 配置参数
 */

(function (factory) {

  window.Kxui = window.kxui = factory();
  if (typeof exports === "object" && typeof module !== "undefined") {
    module.exports = window.Kxui;
  }

}(function () {

  "use strict";

  const win = window;
  const doc = win.document;
  const is_exports = typeof exports === "object" && typeof module !== "undefined";

  const mod_built = ["method", "countdown", "lazy", "queue"];
  const mod_third = ["moment", "jquery"];
  const mod_stock = mod_built.concat(mod_third);
  const mod_info  = {
    name: "Kxui",
    info: "Common Development Function Packaging",
    version: "2.3.0",
    time: {
      create: "2018.05.31",
      update: "2021.08.19"
    },
  };

  /**
   * @method 异常抛出及全局错误样式注册
   * @param  {object} kxui   框架自身
   * @param  {number} error  抛出错误的类型[警告(0)/错误(1)]
   * @param  {number} num    抛出错误文案的编号
   * @param  {string} module 发生错误的模块名称
   */
  win.Kxui_throws_style = ["font-weight:bold;font-size:16px;padding-top:5px;", "color:#BBBBBB;font-size:12px", "font-size:12px;padding:10px;"];
  const throws = (kxui, error, num, module) => {
    if (kxui.configuration.debug) {
      const nums = {};
      nums[0] = `加载方法 {use} 不符合结构规范`;
      nums[1] = `回调方法 {callback} 不符合结构规范`;
      nums[2] = `加载模块 {${module}} 失败，未能找到此模块`;
      nums[3] = `加载模块 {${module}} 失败，此模块已被加载`;
      nums[4] = `加载模块 {${module}} 失败，未能找到 {body} 节点`;
      const text = [`%cKxui%c (${mod_info.version})\n%c${nums[num]}，请检查修改。`, win.Kxui_throws_style[0], win.Kxui_throws_style[1], win.Kxui_throws_style[2]];
      error === 0 ? console.warn(...text) : console.error(...text);
    }
  };

  /**
   * @method 获取引入路径
   * @return {string} 引入路径前缀
   */
  const domain = () => {
    let kxui_domain = doc.scripts;
    let last = kxui_domain.length - 1;
    for (let i = last; i >= 0; i--) {
      let src = kxui_domain[i].src.toLowerCase() || false;
      if (src && src.indexOf("kxui") >= 0) {
        kxui_domain = kxui_domain[i].src.substring(0, kxui_domain[i].src.lastIndexOf("/") + 1);
        break;
      }
    }
    return kxui_domain;
  };

  class Load {

    constructor(Kxui, parameter) {
      this.kxui = Kxui;
      this.parameter = parameter;
      this.module = (typeof this.parameter.module === "string") ? [this.parameter.module] : (typeof this.parameter.module === "object") ? this.parameter.module : false;
      this.callback = (typeof this.parameter.callback === "function") ? this.parameter.callback : false;

      if (!this.module) {
        throws(this.kxui, 1, 0);
      } else if (!this.callback) {
        throws(this.kxui, 1, 1);
      } else {
        this.definition();
      }
    }

    // 定义成员变量
    // 成员变量在整个方法内适用
    definition() {

      // 用于传统模式加载
      // 取代循环 index 值，进行轮询下标的依据
      this.load_line = 0;

      // 用于计数器轮询
      // 当使用传统模式加载，未能找到 body 时，进行最大 100 次计时轮询，确保页面渲染完成
      this.wheel_search = 0;

      // 获取引入 kxui 的位置，以此为依据找寻所属 module 所在的文件夹
      // 前提条件是 module 文件夹必须在 Kxui 同级
      this.domain = domain();

      // 进行模块加载分流
      // 将区分传统模式以及 ES6 模式
      is_exports ? this.imports(): this.shunt();
    }

    // ES6 模式加载
    // 通过 ES6 的 require 方法进行载入需要的模块文件
    // 通过此方法需要将载入回来的模块文件内容再次挂载至 Kxui 内
    imports() {
      for (let i = 0; i < this.module.length; i++) {
        const mod_name = this.module[i].toLowerCase();
        if (!this.kxui[mod_name] && mod_stock.indexOf(mod_name) >= 0) {
          const is_built = mod_built.indexOf(mod_name) >= 0;
          const module = require(`./modules/${is_built ? "built" : "third"}/${mod_name}`);
          this.third_registered(mod_name, module);
        } else if (this.kxui[mod_name]) {
          throws(this.kxui, 0, 3, this.module[i]);
        } else {
          throws(this.kxui, 0, 2, this.module[i]);
        }
      }
      this.end();
    }

    // 传统模式加载
    // 使用传统 script 标签来进行增加模块文件
    // 传统模式加载当前模块为正确模块时进入
    // 进行模块记录，目的达到区别是否存在同名模块
    shunt() {
      const mod_name = this.module[this.load_line].toLowerCase();
      if (!this.kxui[mod_name] && mod_stock.indexOf(mod_name) >= 0) {
        const is_built = mod_built.indexOf(mod_name) >= 0;
        this.create(mod_name, is_built);
      } else if (this.kxui[mod_name]) {
        throws(this.kxui, 0, 3, this.module[this.load_line]);
        this.loop();
      } else {
        throws(this.kxui, 0, 2, this.module[this.load_line]);
        this.loop();
      }
    }

    /**
     * @method 创建模块标签
     * @param  {string}  mod_name 模块名称
     * @param  {boolean} is_built 是否是自身模块
     *
     * 传统模式加载需通过创建 script 标签进行引入相关模块
     */
    create(mod_name, is_built) {
      const body = doc.getElementsByTagName("body")[0];
      const script = doc.createElement("script");

      script.type = "text/javascript";
      script.async = "async";
      script.src = `${this.domain}modules/${is_built ? "built" : "third"}/${mod_name}.js`;

      if (body) {
        body.appendChild(script);
        this.wait(body, script, mod_name);
      } else if (this.wheel_search < 100) {
        setTimeout(() => {
          this.wheel_search = this.wheel_search + 1;
          this.create(mod_name, is_built);
        }, 10);
      } else {
        throws(this.kxui, 1, 4, mod_name);
        this.end();
      }
    }

    /**
     * @method 等待当前模块加载完毕
     * @param  {object} body     结构对象
     * @param  {object} script   等待的标签对象
     * @param  {string} mod_name 模块名称
     *
     * 为确保模块之间先后顺序的依赖关系，只有当模块加载完成之后才进行后续模块操作
     */
    wait(body, script, mod_name) {

      // 加载完成之后操作
      // 移除加载模块所创建的 script 标签
      // 重新进入 loop 加载环路
      const wait_end = () => {
        body.removeChild(script);
        this.loop();
      };

      // 兼容性判断
      // 判断当时模块是否已经加载完毕
      if (script.readyState) {
        script.onreadystatechange = () => {
          if (script.readyState === "complete" || script.readyState === "loaded") {
            script.onreadystatechange = null;
            this.third_registered(mod_name);
            wait_end();
          }
        };
      } else {
        script.onload = () => {
          this.third_registered(mod_name);
          wait_end();
        };
      }
    }

    /**
     * @method 模块注册/第三方模块注册
     * @param  {string} mod_name 模块名称
     * @param  {object} content  模块内容
     *
     * 模块注册只在 ES6 模式下才会生效
     * 模块内容只在 ES6 模式下才会生效
     * 模块注册只在注册第三方模块时生效
     * 传统模式在加载模块文件后，模块文件内部将会自行关联 Kxui，故无需多余注册
     * 当使用第三方模块时，需挂在在 window 上的对象名与模块名一致，暂时不支持非内置模块加载
     * 当加载 jquery 时，将会被处理成挂载小写名称且均会移除模块挂载在 window 上的全局变量
     */
    third_registered(mod_name, content) {
      const delete_window_registered = this.kxui.configuration.delete_window_registered;
      if (!this.kxui[mod_name]) {
        if (content) {
          this.kxui[mod_name] = content;
        } else if (win[mod_name]) {
          this.kxui[mod_name] = win[mod_name];
          if (delete_window_registered) { delete win[mod_name]; }
        } else if (mod_name === "jquery") {
          this.kxui[mod_name] = win.$;
          if (delete_window_registered) {
            delete win.$;
            delete win.jQuery;
          }
        } else {
          throws(this.kxui, 1, 2, mod_name);
        }
      }
    }

    // 加载环路
    // 传统模式加载当前模块完成或异常时进入
    // 结束当前模块，循环进入后续模块加载
    loop() {
      this.load_line = this.load_line + 1;
      if (this.module[this.load_line]) {
        this.shunt();
      } else {
        this.end();
      }
    }

    // 结尾方法
    // 回调 Kxui 结构下的 callback 方法
    end () {
      this.callback();
    }
  }

  class Kxui {

    constructor() {
      this.about = mod_info;
    }

    /**
     * @method 模块加载
     * @param  {string/array} module   模块名称
     * @param  {function}     callback 加载完成回调方法
     *
     * Kxui.use(module, callback)
     * 可通过此方法进行内置模块加载
     * 内置模块加载可分为传统模式与 ES6 模式
     */
    use(module, callback) {
      const data = {};
      data.module = module;
      data.callback = callback;

      // 进行全局参数配置
      this.config();

      // 进行逻辑层装载
      // 所有逻辑将会依托于此构造函数
      new Load(this, data);
    }

    /**
     * @method 全局参数配置
     * @param  {object} parameter 配置参数
     *
     * Kxui.config(parameter)
     * 此方法将会改变全局参数配置，全局只在同一个 Kxui 内生效
     * 若是不同页面分别引入且页面无任何关联关系，全局配置将只作用于各自所属的页面
     */
    config(parameter) {
      const par = (typeof parameter === "object" ? parameter : {});
      const configuration = this.configuration || {};

      this.configuration = {
        // 是否异常抛出
        // 建议根据环境变量在开发环境下开启，在生产环境下则进行关闭
        debug: typeof par.debug === "boolean" ? par.debug : typeof configuration.debug === "boolean" ? configuration.debug : true,

        // 是否在第三方模块注册完成后移除其挂载在 window 上的全局变量
        delete_window_registered: typeof par.delete_window_registered === "boolean" ? par.delete_window_registered : typeof configuration.delete_window_registered === "boolean" ? configuration.delete_window_registered : true
      };
    }
  }

  return new Kxui();

}));
